package org.stragma.apply.modules

import org.kiama.rewriting._
import org.kiama.rewriting.Rewriter.{all,attempt,innermost,rulefs,Strategy,Term,topdown}

// Import the required signatures, these are obtained from running sig2scala on the Stratego signatures
import org.stragma.apply.signatures.Apply._
import org.stragma.apply.util._

import org.stragma.util.ListManager._

object Desugar extends TermInputOutput {
	
	/*
	* These are the only rules defined in Desugar.str. Other rules are encapsulated
	* in the definition of strategies using ?;!
	* Stratego Rules
	* DesugarInOut : In(x,t)        -> Argument(x,Access("In",t))
   	* DesugarInOut : Out(x,t)       -> Argument(x,Access("Out",t))
   	* DesugarInOut : InOut(x,t)     -> Argument(x,Access("InOut",t))
	*/
	def DesugarInOut = 
		rulefs {
			case In(x,t) 		=> Argument1(x, Access("In",t))
			case Out(x,t) 		=> Argument1(x,Access("Out",t))
			case InOut(x,t) 	=> Argument1(x,Access("InOut",t))
		}
	
	/*
	 * Stratego Strategy
	 * DesugarRange : Range(e) -> Range(e,e)
	 */
	def desugarRange = 
		rulefs {
			case Range(x)			=> Range1(x,x)
		}
	
	/*
	* Stratego Strategy
	* DesugarVar = ?[Declaration(VarList([x|y]),type)|a]; ![Declaration(x,type)|<DesugarVar>[Declaration(VarList(y),type)|a] ]
   	* DesugarVar = ?[Declaration(VarList([]),type)|a]; !<DesugarVar> a
   	* DesugarVar = []
	*/
	def desugarVar : Strategy = 
		rulefs {
			case Declaration(VarList(x :: Nil),z : TType) :: Nil =>
				List(Declaration1(x,z))
			case Declaration(VarList(x :: y),z : TType) :: a =>
				val decl = List(Declaration1(x,z),(attempt(desugarVar)(Declaration(VarList(y),z) :: a)).get)
				// Unlike in the Stratego version in Kiama we need to remove nested Lists which are added by the recursive function
				flattenTerm(decl)
			case Declaration(VarList(Nil),z : TType) :: a =>
				(attempt(desugarVar)(a)).get
		}
	
	/*
	* Stratego Strategy
	* DesugarArg = ?[Argument(VarList([x|y]),type)|a]; ![Argument(x,type)|<DesugarArg>[Argument(VarList(y),type)|a] ]
   	* DesugarArg = ?[Argument(VarList([]),type)|a]; !<DesugarArg> a
   	* DesugarArg = []
	*/
	def desugarArg : Strategy = 
		rulefs {
			case Argument1(VarList(x :: Nil),z : TArgType) :: Nil =>
				List(Argument(x,z))
			case Argument1(VarList(x :: y),z : TArgType) :: a =>			
				val args = List(Argument(x,z),(attempt(desugarArg)(Argument1(VarList(y),z) :: a)).get)
				// Unlike in the Stratego version in Kiama we need to remove nested Lists which are added by the recursive function
				flattenTerm(args)
			case Argument1(VarList(Nil),z : TArgType) :: a =>
				(attempt(desugarArg)(a)).get
		}
	
	/*
	 * Stratego Strategy
	 * DesugarArgVar = try(DesugarVar);try(DesugarArg);all(DesugarArgVar)
	 */
	def desugarArgVar : Strategy = attempt(desugarVar) <* attempt(desugarArg) <* all(desugarArgVar)
	
	/*
	 * Stratego Strategy
	 * Apply_Desugar = topdown(try(DesugarInOut)) ; try(DesugarArgVar); innermost(DesugarRange)
	 */
	def applyDesugar = topdown(attempt(DesugarInOut)) <* attempt(desugarArgVar) <* innermost(desugarRange)
	
	/*
	 * Helper method to invoke function from Java
	 */
	def applyDesugarHelper(term : Term) : Term = (applyDesugar(term)).get
	
	/*
	 * This helper method executes applyDesugar n times and returns
	 * the very last result the caller
	 */
	def applyDesugarHelper(term : Term, n : scala.Int) : Term = {
		var result : Term = scala.None
		for(i <- 0 until n) {
			result = (applyDesugar(term)).get
			
			// Added the println to mimic the way strategoxt.jar works
			println(result)
		}
		
		result
	}
	
	/*
	 * This helper method executes applyDesugar n times and returns
	 * the very last result to the caller
	 */
	def applyDesugarHelper(file : String, n : scala.Int) : Term = {
		val iTerm = loadTermFromFile(file)
		applyDesugarHelper(iTerm, n)
	}
}